Fair queuing is a family of scheduling algorithms used in some process and network schedulers. The algorithm is designed to achieve Fairness measure when a limited resource is shared, for example to prevent flows with large packets or processes that generate small jobs from consuming more throughput or CPU time than other flows or processes.
Fair queuing is implemented in some advanced and routers.
A byte-weighted version was proposed by Alan Demers, Srinivasan Keshav and Scott Shenker in 1989, and was based on the earlier Nagle fair queuing algorithm. The byte-weighted fair queuing algorithm aims to mimic a bit-per-bit multiplexing by computing theoretical departure date for each packet.
The concept has been further developed into weighted fair queuing, and the more general concept of traffic shaping, where queuing priorities are dynamically controlled to achieve desired flow quality of service goals or accelerate some flows.
The advantage over conventional first in first out (FIFO) or priority queuing is that a high-data-rate flow, consisting of large packets or many data packets, cannot take more than its fair share of the link capacity.
Fair queuing is used in routers, switches, and statistical multiplexers that forward packets from a buffer. The buffer works as a queuing system, where the data packets are stored temporarily until they are transmitted.
With a link data-rate of R, at any given time the N active data flows (the ones with non-empty queues) are serviced each with an average data rate of R/N. In a short time interval the data rate may fluctuate around this value since the packets are delivered sequentially in turn.
The complexity of the algorithm is O(log(n)), where n is the number of queues/flows.
To reduce computational load, the concept of virtual time is introduced. Finish time for each packet is computed on this alternate monotonically increasing virtual timescale. While virtual time does not accurately model the time packets complete their transmissions, it does accurately model the order in which the transmissions must occur to meet the objectives of the full-featured model. Using virtual time, it is unnecessary to recompute the finish time for previously queued packets. Although the finish time, in absolute terms, for existing packets is potentially affected by new arrivals, finish time on the virtual time line is unchanged - the virtual time line warps with respect to real time to accommodate any new transmission.
The virtual finish time for a newly queued packet is given by the sum of the virtual start time plus the packet's size. The virtual start time is the maximum between the previous virtual finish time of the same queue and the current instant.
With a virtual finishing time of all candidate packets (i.e., the packets at the head of all non-empty flow queues) computed, fair queuing compares the virtual finishing time and selects the minimum one. The packet with the minimum virtual finishing time is transmitted.
'''Shared variables''' const N // Nb of queues queues[1..N] // queues lastVirFinish[1..N] // last virtual finish instant | |
'''receive'''(packet) queueNum := chooseQueue(packet) queues[queueNum].enqueue(packet) updateTime(packet, queueNum) | '''updateTime'''(packet, queueNum) // virStart is the virtual start of service virStart := max(now(), lastVirFinish[queueNum]) packet.virFinish := packet.size + virStart lastVirFinish[queueNum] := packet.virFinish |
'''send()''' queueNum := selectQueue() packet := queues[queueNum].dequeue() '''return''' packet | '''selectQueue()''' it := 1 minVirFinish = '''while''' it ≤ N '''do''' queue := queues[it] '''if''' '''not''' queue.empty '''and''' queue.head.virFinish < minVirFinish '''then''' minVirFinish = queue.head.virFinish queueNum := it it := it + 1 '''return''' queueNum |
The function receive() is executed each time a packet is received, and send() is executed each time a packet to send must be selected, i.e. when the link is idle and the queues are not empty. This pseudo-code assumes there is a function now() that returns the current virtual time, and a function chooseQueue() that selects the queue where the packet is enqueued.
The function selectQueue() selects the queue with the minimal virtual finish time. For the sake of readability, the pseudo-code presented here does a linear search. But maintaining a sorted list can be implemented in logarithmic time, leading to a O(log(n)) complexity, but with more complex code.
|
|